package crud

import (
	"context"
	"gitee.com/baizhige/gin-common-handler/handler"
	"gitee.com/baizhige/gin-common-handler/params"
	"gitee.com/baizhige/go-server-tempalte/app_params"
	"gitee.com/baizhige/go-server-tempalte/common/constant"
	"gitee.com/baizhige/go-server-tempalte/common/log"
	"gitee.com/baizhige/mongong-go"
	"gitee.com/baizhige/mongong-go/collections"
	"gitee.com/baizhige/mongong-go/db"
	"gitee.com/baizhige/mongong-go/query"
	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
)

func Default[T collections.CollectionName](router gin.IRouter) *gin.RouterGroup {
	var t T
	group := router.Group("/" + t.CollectionName())
	handler.Select(group)
	insert[T]()
	deleteById[T]()
	modify[T]()
	page[T]()
	return group
}

func Use[T collections.CollectionName](router gin.IRouter, opts ...string) *gin.RouterGroup {
	var t T
	group := router.Group("/" + t.CollectionName())
	handler.Select(group)
	for _, item := range opts {
		switch item {
		case OptInsert:
			insert[T]()
		case OptModify:
			modify[T]()
		case OptDeleteById:
			deleteById[T]()
		case OptPage:
			page[T]()
		}
	}
	return group
}

const OptInsert = "insert"

func insert[T collections.CollectionName]() {
	handler.POST("/insert", func(traceId string, t T) *mongo.InsertOneResult {
		res, err := mongong.Coll[T]().InsertOne(t)
		if err != nil {
			log.ErrorTF(traceId, "insert error %v", err)
			panic(constant.ErrInsert)
		}
		return res
	}, app_params.TraceId(), params.Body[T]())
}

const OptDeleteById = "deleteById"

func deleteById[T collections.CollectionName]() {
	handler.DELETE("/delete/:id", func(traceId string, id string) {
		var oriId interface{}
		hex, err := primitive.ObjectIDFromHex(id)
		if err != nil {
			oriId = id
		} else {
			oriId = hex
		}
		q := query.Eq("_id", oriId)
		res, err := mongong.Coll[T]().DeleteOne(q)
		if err != nil {
			log.ErrorTF(traceId, "DeleteByQuery delete data error %v", err)
			panic(constant.ErrDeleteData)
		}

		var t T
		copyColl, err := db.Collection(db.DefaultDBKey, t.CollectionName()+"_copy")
		if err != nil {
			log.ErrorTF(traceId, "DeleteByQuery connection collection error %v", err)
			panic(constant.ErrCopyData)
		}

		_, err = copyColl.InsertOne(context.Background(), res)
		if err != nil {
			log.ErrorTF(traceId, "DeleteByQuery copy data error %v", err)
			panic(constant.ErrCopyData)
		}

	}, app_params.TraceId(), params.Path("id", params.STRING))
}

const OptModify = "modify"

func modify[T collections.CollectionName]() {
	handler.PUT("/modify/:id", func(traceId string, id string, obj T) *mongo.UpdateResult {
		var oriId interface{}
		hex, err := primitive.ObjectIDFromHex(id)
		if err != nil {
			oriId = id
		} else {
			oriId = hex
		}
		q := query.Eq("_id", oriId)
		one, err := mongong.Coll[T]().ModifyOne(q.Filter, obj)
		if err != nil {
			log.ErrorTF(traceId, "ModifyById modify data error %v", err)
			panic(constant.ErrUpdateOne)
		}
		return one
	}, app_params.TraceId(), params.Path("id", params.STRING), params.Body[T]())
}

const OptPage = "page"

func page[T collections.CollectionName]() {
	handler.POST("/page", func(traceId string, q query.Query) *query.PageData[T] {
		res, err := mongong.Coll[T]().QueryPage(&q)
		if err != nil {
			log.ErrorTF(traceId, "query page err:%v", err)
			panic(constant.ErrQueryPage)
		}
		return res
	}, app_params.TraceId(), params.Body[query.Query]())
}
